namespace SimpleWebServices.Data
{
    using System;
    using System.Xml;
    using System.Xml.Schema;
    using System.Xml.Serialization;
    using SimpleWebServices.Configuration;

    [XmlRoot("link")]
    public sealed class Link<T> : IXmlSerializable
        where T : IRecord
    {
        public Link()
        {
        }

        public Link(AbsoluteUri urn)
        {
            this.Urn = urn;
        }

        public T Get
        {
            get
            {
                T value = default(T);

                if (null != this.Urn)
                {
                    value = Record.Retrieve<T>(this.Urn);
                }

                return value;
            }
        }

        public AbsoluteUri Urn
        {
            get;
            set;
        }

        public static bool operator ==(Link<T> obj, Link<T> comparand)
        {
            bool result = false;

            if (object.ReferenceEquals(null, obj))
            {
                result = object.ReferenceEquals(null, comparand);
            }
            else
            {
                result = obj.Equals(comparand);
            }

            return result;
        }

        public static bool operator !=(Link<T> obj, Link<T> comparand)
        {
            bool result = false;

            if (object.ReferenceEquals(null, obj))
            {
                result = !object.ReferenceEquals(null, comparand);
            }
            else
            {
                result = !obj.Equals(comparand);
            }

            return result;
        }

        public static implicit operator T(Link<T> value)
        {
            return (null == value) ? default(T) : value.Get;
        }

        public static implicit operator Link<T>(T value)
        {
            Link<T> result = null;

            if (null != value)
            {
                result = new Link<T>();
                result.Set(value);
            }

            return result;
        }

        public void Set(IRecord record)
        {
            if (null == record)
            {
                throw new ArgumentNullException("record");
            }

            this.Urn = record.Urn;
        }

        public override bool Equals(object obj)
        {
            bool result = false;

            if (!object.ReferenceEquals(null, obj))
            {
                if (object.ReferenceEquals(this, obj))
                {
                    result = true;
                }
                else
                {
                    Link<T> cast = obj as Link<T>;

                    if (!object.ReferenceEquals(null, cast))
                    {
                        result = true;
                        result = 0 == string.CompareOrdinal(this.Urn, cast.Urn);
                    }
                }
            }

            return result;
        }

        public override int GetHashCode()
        {
            return this.ToString().GetHashCode();
        }

        public override string ToString()
        {
            return this.Serialize().CreateNavigator().OuterXml;
        }

        XmlSchema IXmlSerializable.GetSchema()
        {
            throw new NotSupportedException();
        }

        void IXmlSerializable.ReadXml(XmlReader reader)
        {
            if (null == reader)
            {
                throw new ArgumentNullException("reader");
            }

            string value = reader.GetAttribute("urn");
            if (!string.IsNullOrEmpty(value))
            {
                this.Urn = value;
            }
        }

        void IXmlSerializable.WriteXml(XmlWriter writer)
        {
            if (null == writer)
            {
                throw new ArgumentNullException("writer");
            }

            if (null != this.Urn)
            {
                writer.WriteAttributeString("urn", this.Urn);
            }
        }
    }
}